/*
 * Copyright (C) 2012 rothens
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package rpg.entities;

import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import rpg.game.SingleGame;
import rpg.Location;
import rpg.RPG;
import rpg.console.Console;
import rpg.gfx.Bitmap;
import rpg.game.GameScreen;
import rpg.gfx.gui.Bar;
import rpg.gfx.gui.Text;
import rpg.gfx.menu.MainMenu;
import rpg.inventory.Inventory;
import rpg.util.ImageUtil;

/**
 *
 * @author rothens
 */
public class Player extends LivingEntity {

    public static final int UP = 0;
    public static final int LEFT = 1;
    public static final int DOWN = 2;
    public static final int RIGHT = 3;
    private static Bitmap[] playerBM;
    private int energy = 100;
    private SingleGame game;
    private int xOffs;
    private int yOffs;
    private int dir;
    private boolean moving;
    private boolean running;
    private boolean resting;
    private static int animphase;
    private Bar energyBar;
    private Bar xpBar;
    private int animDelay = 5;
    private int restDelay = 20;
    private int consumeDelay = 5;
    private int damageDelay = 0;
    private int healdelay = 100;
    private Inventory inventory;
    private Text levelText;
    private static Player instance = null;
    private boolean immortal = false;
    private int currXP;
    private int unspentTalentPoint;

    private Player(final int health, final Location loc) {
        super(health, loc);
        energyBar = new Bar((int) RPG.WINDOW_WIDTH / 64 * 10, 6, 0xffff00, 0x0, 100, 100);
        healthbar = new Bar((int) RPG.WINDOW_WIDTH / 64 * 10, 6, 0xff0000, 0x1, 150, 150);
        xpBar = new Bar((int) RPG.WINDOW_WIDTH / 64 * 50, 6, 0x00ff00, 0x1, 100, 0);
        levelText = new Text(0x00ff00, 500, 20);
        GameScreen.getInstance().gui.addElement(energyBar, 10, RPG.WINDOW_HEIGHT - 20);
        GameScreen.getInstance().gui.addElement(healthbar, 10, RPG.WINDOW_HEIGHT - 30);
        GameScreen.getInstance().gui.addElement(xpBar, (int) RPG.WINDOW_WIDTH / 64 * 10 + 20, RPG.WINDOW_HEIGHT - 20);
        GameScreen.getInstance().gui.addElement(levelText, (int) RPG.WINDOW_WIDTH / 64 * 10 + 20, RPG.WINDOW_HEIGHT - 45);
    }

    public static synchronized Player getInstance() {
        if (instance == null) {
            throw new NullPointerException();
        }
        return instance;
    }

    public static synchronized void newInstance(Location loc, SingleGame actual) {
        instance = new Player(150, loc);
        //instance.setEnergyBar(new Bar(100, 6, 0xffff00, 0x0, 100, 100));
        playerBM = new Bitmap[12];
        instance.setGame(actual);
        instance.setDir(DOWN);

        animphase = 0;
        BufferedImage image = ImageUtil.createImageIcon("/res/entities/character.png");//.getRGB(0, 0, playerBM.width, playerBM.height, playerBM.pixels, 0, playerBM.width);
        for (int i = 0; i < 12; i++) {
            int col = i % 3;
            int row = i / 3;
            playerBM[i] = new Bitmap(64, 64);
            image.getSubimage(col * 64, row * 64, 64, 64).getRGB(0, 0, playerBM[i].width, playerBM[i].height, playerBM[i].pixels, 0, playerBM[i].width);
        }
        //Game.eh.addToActive(instance);
    }

    private void checkControls() {
        if (RPG.keylistener.isKeyDown2(KeyEvent.VK_ESCAPE)) {
            Console c = Console.getInstance();
            if (c.isVisible()) {
                c.toggle();
            } else {
                RPG.rpg.gotoMenu(new MainMenu(RPG.WINDOW_WIDTH, RPG.WINDOW_HEIGHT));
            }
        }
        int moves = 0;
        int tmp = -1;

        running = RPG.keylistener.isKeyDown(KeyEvent.VK_SHIFT);
        int speed = (running && energy > 0 && !resting) ? 3 : 2;
        if (RPG.keylistener.isKeyDown(KeyEvent.VK_0) && RPG.keylistener.isKeyDown(KeyEvent.VK_SHIFT)) {
            RPG.keylistener.clearCache();
            RPG.keylistener.clear();
            Console con = Console.getInstance();
            con.toggle();
        }
        if (!Console.getInstance().isVisible()) {
            if (RPG.keylistener.isKeyDown(KeyEvent.VK_A) || RPG.keylistener.isKeyDown(KeyEvent.VK_LEFT)) {
                if (currentLocation.move(Location.LEFT, speed)) {
                    dir = LEFT;
                    moves++;
                } else {
                    tmp = LEFT;
                }
            } else if (RPG.keylistener.isKeyDown(KeyEvent.VK_D) || RPG.keylistener.isKeyDown(KeyEvent.VK_RIGHT)) {
                if (currentLocation.move(Location.RIGHT, speed)) {
                    dir = RIGHT;
                    moves++;
                } else {
                    tmp = RIGHT;
                }
            }
            if (RPG.keylistener.isKeyDown(KeyEvent.VK_W) || RPG.keylistener.isKeyDown(KeyEvent.VK_UP)) {
                if (currentLocation.move(Location.UP, speed)) {
                    dir = UP;
                    moves++;
                } else {
                    tmp = UP;
                }
            } else if (RPG.keylistener.isKeyDown(KeyEvent.VK_S) || RPG.keylistener.isKeyDown(KeyEvent.VK_DOWN)) {
                if (currentLocation.move(Location.DOWN, speed)) {
                    dir = DOWN;
                    moves++;
                } else {
                    tmp = DOWN;
                }
            }
            if (RPG.keylistener.isKeyDown(KeyEvent.VK_SPACE)) {
                /*
                 * interact();
                 */
            }

            if (RPG.keylistener.isKeyDown(KeyEvent.VK_CONTROL)) {
                attack();
            }

            if (RPG.keylistener.isKeyDown(KeyEvent.VK_PRINTSCREEN) || RPG.keylistener.isKeyDown(KeyEvent.VK_F12)) {
                RPG.rpg.screenShot();
            }

            if (!SingleGame.getInstance().isPaused() && RPG.keylistener.isKeyDown2(KeyEvent.VK_P)) {
                SingleGame.getInstance().setPaused(true);
            }

            moving = moves > 0;
            if (moving && running && energy > 0 && !resting) {
                consumeDelay--;
                if (consumeDelay <= 0) {
                    consumeDelay = 5;
                    energy -= 1;
                    if (energy < 2) {
                        resting = true;
                    }
                }
            }
            if (!moving && tmp >= 0) {
                dir = tmp;
            }
        }

    }

    private void attack() {

        if (damageDelay != 0) {
            return;
        }
        //Change Maps.getentity i a good way to check for a range of X between players location and the target
        List<Entity> entity = SingleGame.getInstance().getActual().getEntity(getCurrentLocation().getTile(dir), getCurrentLocation().getExactLocation());
        for (Entity e : entity) {
            if (e instanceof LivingEntity) {
                LivingEntity le = (LivingEntity) e;
                damageDelay = 30;
                Random r = new Random();
                int dmg = r.nextInt(10) + 10;
                boolean damage = le.damage(this, dmg);
                if (damage) {
                    if (e != null) {
                        accountXp(le.getLevel());
                        EntityHandler.getInstance().removeEntity(e);
                        //SpriteFactory.remove(((Sprite) e).getProp().getId());
                    }
                }
            }

        }

    }

    private void checkMap() {
        /*if (currentLocation.getX() < RPG.WINDOW_WIDTH / 2) {
            setxRenderOffset(RPG.WINDOW_WIDTH / 2 - currentLocation.getX());
        } else if (currentLocation.getX() > SingleGame.getInstance().getActual().getWidth() * 64 - RPG.WINDOW_WIDTH / 2) {
            setxRenderOffset((SingleGame.getInstance().getActual().getWidth() * 64 - RPG.WINDOW_WIDTH / 2) - currentLocation.getX());
        } else {
            setxRenderOffset(0);
        }

        if (currentLocation.getY() < RPG.WINDOW_HEIGHT / 2) {
            setyRenderOffset(RPG.WINDOW_HEIGHT / 2 - currentLocation.getY());
        } else if (currentLocation.getY() > SingleGame.getInstance().getActual().getHeight() * 64 - RPG.WINDOW_HEIGHT / 2) {
            setyRenderOffset((SingleGame.getInstance().getActual().getHeight() * 64 - RPG.WINDOW_HEIGHT / 2) - currentLocation.getY());
        } else {
            setyRenderOffset(0);
        }*/
    }

    @Override
    public Bitmap getDrawGraphics() {
        if (moving) {
            return playerBM[animphase % 2 + dir * 3 + 1];
        } else {
            return playerBM[dir * 3];
        }
    }

    @Override
    public boolean isPassable() {
        return false;
    }

    @Override
    public boolean tick() {
        if (!SingleGame.getInstance().isPaused()) {
            animDelay--;
            if (animDelay <= 0) {
                animDelay = 5;
                animphase++;
                animphase %= 2;
            }
            healdelay--;
            if (healdelay <= 0) {
                healdelay = 100;
                if (!running || resting) {
                    if (health < 150) {
                        int from = health;
                        health += 1;
                        if (from < 30 && energy >= 30) {
                            resting = false;
                        }
                    }
                }
            }
            restDelay--;
            if (restDelay <= 0) {
                restDelay = 20;
                if (!running || resting) {
                    if (energy < 100) {
                        int from = energy;
                        energy++;
                        if (from < 30 && energy >= 30) {
                            resting = false;
                        }
                    }
                }
            }
            if (damageDelay > 0) {
                damageDelay--;
            }
            checkControls();
            checkMap();
            
            energyBar.setVal(energy);
            
            healthbar.setVal(health);
            levelText.setText("" + level);
            if (level <= 40) {
                xpBar.setVal(currXP);
            } else {
                xpBar.setColor(0xeac117);
            }
        } else {
            checkMap();
        }
        return true;
    }

    public void interact() {
        List<Entity> entity = game.actual.getEntity(currentLocation.getTile(dir), getCurrentLocation().getExactLocation());
        for (Entity e : entity) {
            /*if (e instanceof Chest) {
             ((Chest) e).open(this);
             }*/
        }
    }

    public void tryDamage() {
        if (damageDelay != 0) {
            return;
        }

        List<Entity> entity = game.actual.getEntity(getCurrentLocation().getTile(dir), getCurrentLocation().getExactLocation());
        for (Entity e : entity) {
            if (e instanceof LivingEntity) {
                damageDelay = 30;
                Random r = new Random();
                int dmg = r.nextInt(10) + 10;
                LOG.log(Level.OFF, String.valueOf(dmg));

                boolean damage = ((LivingEntity) e).damage(this, dmg);
                if (damage) {
                    game.actual.entities.remove(e);
                }
            }
        }
    }

    public void setEnergy(int energy) {
        this.energy = energy;
    }

    public void setDir(int dir) {
        this.dir = dir;
    }

    public void setEnergyBar(Bar energyBar) {
        this.energyBar = energyBar;
    }

    public void setGame(SingleGame game) {
        this.game = game;
    }
    private static final Logger LOG = Logger.getLogger(Player.class.getName());

    public int getxOffs() {
        return xOffs;
    }

    public int getyOffs() {
        return yOffs;
    }

    public Bar getEnergyBar() {
        return energyBar;
    }

    @Override
    public boolean damage(LivingEntity causer, int dmg) {
        if (immortal) {
            dmg = 0;
        }
        if (health - dmg <= 0) {
            health = 0;
            RPG.rpg.playerDeath();
        } else {
            health -= dmg;
            healthbar.setVal(health);
        }
        //lastDamagedBy = causer;
        return health == 0;
    }

    public boolean toggleImmortal() {
        immortal = !immortal;
        return immortal;
    }

    private void checklevelup() {
        if (currXP >= 100) {
            level += currXP / 100;
            currXP = currXP % 100;
            unspentTalentPoint += (currXP / 100) * 4;
        }
    }

    public int getCurrXP() {
        return currXP;
    }

    public void accountXp(int xpBoost) {
        if (level <= 40) {
            this.currXP += xpBoost;
            checklevelup();
        }
    }
}
